const jwt = require('jsonwebtoken');
const { authenticateToken, checkRole, generateToken } = require('../../middleware/auth');
const TestUtils = require('../testUtils');

// 模拟数据库查询
const mockQuery = jest.fn();

// 模拟数据库模块
jest.mock('../../config/database', () => ({
  query: (...args) => mockQuery(...args)
}));

describe('Auth Middleware Tests', () => {
  let mockReq;
  let mockRes;
  let mockNext;

  beforeEach(() => {
    TestUtils.setupTestEnvironment();
    
    mockReq = {
      headers: {},
      user: null
    };
    
    mockRes = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn()
    };
    
    mockNext = jest.fn();
    
    // 重置模拟函数
    mockQuery.mockReset();
  });

  afterEach(() => {
    TestUtils.resetTestEnvironment();
  });

  describe('generateToken', () => {
    test('应该生成有效的JWT token', () => {
      const userId = 123;
      const token = generateToken(userId);
      
      expect(token).toBeDefined();
      expect(typeof token).toBe('string');
      
      // 验证token可以被正确解码
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      expect(decoded.userId).toBe(userId);
    });

    test('生成的token应该包含正确的过期时间', () => {
      const userId = 123;
      const token = generateToken(userId);
      
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      expect(decoded.exp).toBeDefined();
      expect(decoded.iat).toBeDefined();
    });
  });

  describe('authenticateToken', () => {
    test('应该成功验证有效的token', async () => {
      const testUser = TestUtils.getTestUser();
      const token = TestUtils.generateTestToken(testUser.id);
      
      mockReq.headers.authorization = `Bearer ${token}`;
      mockQuery.mockResolvedValue([testUser]);

      await authenticateToken(mockReq, mockRes, mockNext);

      expect(mockQuery).toHaveBeenCalledWith(
        'SELECT id, username, email, role, status FROM users WHERE id = ? AND status = "active"',
        [testUser.id]
      );
      expect(mockReq.user).toEqual(testUser);
      expect(mockNext).toHaveBeenCalled();
      expect(mockRes.status).not.toHaveBeenCalled();
    });

    test('应该拒绝没有token的请求', async () => {
      await authenticateToken(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '访问令牌缺失' });
      expect(mockNext).not.toHaveBeenCalled();
    });

    test('应该拒绝无效的token格式', async () => {
      mockReq.headers.authorization = 'InvalidToken';

      await authenticateToken(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(403);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '无效的访问令牌' });
      expect(mockNext).not.toHaveBeenCalled();
    });

    test('应该拒绝已过期的token', async () => {
      const expiredToken = jwt.sign(
        { userId: 1 },
        process.env.JWT_SECRET,
        { expiresIn: '-1h' }
      );
      
      mockReq.headers.authorization = `Bearer ${expiredToken}`;

      await authenticateToken(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '访问令牌已过期' });
      expect(mockNext).not.toHaveBeenCalled();
    });

    test('应该拒绝不存在的用户', async () => {
      const token = TestUtils.generateTestToken(999);
      
      mockReq.headers.authorization = `Bearer ${token}`;
      mockQuery.mockResolvedValue([]);

      await authenticateToken(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '用户不存在或已被禁用' });
      expect(mockNext).not.toHaveBeenCalled();
    });

    test('应该拒绝被禁用的用户', async () => {
      const disabledUser = TestUtils.getTestUser({ status: 'inactive' });
      const token = TestUtils.generateTestToken(disabledUser.id);
      
      mockReq.headers.authorization = `Bearer ${token}`;
      mockQuery.mockResolvedValue([]);

      await authenticateToken(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '用户不存在或已被禁用' });
      expect(mockNext).not.toHaveBeenCalled();
    });
  });

  describe('checkRole', () => {
    test('应该允许具有正确角色的用户访问', () => {
      const adminUser = TestUtils.getTestAdmin();
      mockReq.user = adminUser;
      
      const adminMiddleware = checkRole(['admin']);
      adminMiddleware(mockReq, mockRes, mockNext);

      expect(mockNext).toHaveBeenCalled();
      expect(mockRes.status).not.toHaveBeenCalled();
    });

    test('应该拒绝没有用户信息的请求', () => {
      const adminMiddleware = checkRole(['admin']);
      adminMiddleware(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(401);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '用户未认证' });
      expect(mockNext).not.toHaveBeenCalled();
    });

    test('应该拒绝权限不足的用户', () => {
      const regularUser = TestUtils.getTestUser();
      mockReq.user = regularUser;
      
      const adminMiddleware = checkRole(['admin']);
      adminMiddleware(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(403);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '权限不足' });
      expect(mockNext).not.toHaveBeenCalled();
    });

    test('应该支持多个角色', () => {
      const adminUser = TestUtils.getTestAdmin();
      mockReq.user = adminUser;
      
      const multiRoleMiddleware = checkRole(['admin', 'manager']);
      multiRoleMiddleware(mockReq, mockRes, mockNext);

      expect(mockNext).toHaveBeenCalled();
      expect(mockRes.status).not.toHaveBeenCalled();
    });

    test('应该拒绝不在允许角色列表中的用户', () => {
      const regularUser = TestUtils.getTestUser();
      mockReq.user = regularUser;
      
      const multiRoleMiddleware = checkRole(['admin', 'manager']);
      multiRoleMiddleware(mockReq, mockRes, mockNext);

      expect(mockRes.status).toHaveBeenCalledWith(403);
      expect(mockRes.json).toHaveBeenCalledWith({ message: '权限不足' });
      expect(mockNext).not.toHaveBeenCalled();
    });
  });
}); 